package com.aisino.api.filter;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.aisino.api.config.EntityCacheConfig;
import com.aisino.api.model.DevLog;
import com.aisino.api.model.DrugResponseBase;
import com.aisino.api.model.Entity;
import com.aisino.api.model.ResultCode;
import com.aisino.api.service.DevLogService;
import com.aisino.api.util.CommonIpAddressUtil;
import com.aisino.api.util.IdWorker;
import com.aisino.api.util.SM3SignatureUtil;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.util.ContentCachingResponseWrapper;

import java.io.IOException;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;

public class ApiRequestFilter implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(ApiRequestFilter.class);

    private DevLogService devLogService;
    
    private JdbcTemplate jdbcTemplate;

    public void setDevLogService(DevLogService devLogService) {
        this.devLogService = devLogService;
    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        RequestWrapper requestWrapper = null;
        if (httpRequest instanceof RequestWrapper) {
            requestWrapper = (RequestWrapper) httpRequest;
        } else {
            requestWrapper = new RequestWrapper(httpRequest);
        }

        String method = httpRequest.getMethod();
        String uri = httpRequest.getRequestURI();

        // 只对API路径进行处理
        if (!uri.startsWith("/ygydapi/v1/")) {
            chain.doFilter(request, response);
            return;
        }
        
        // 使用RequestWrapper确保请求体可重复读取
        String requestBody = requestWrapper.getBody();
//        logger.info("API请求-URI: {},请求内容: {}", uri,requestBody);

        // 从请求体中解析参数
        Map<String, Object> requestMap = new HashMap<>();
        if (requestBody != null && !requestBody.isEmpty() && JSONUtil.isTypeJSON(requestBody)) {
            requestMap = JSONUtil.toBean(requestBody, Map.class);
        }
        long startTime = System.currentTimeMillis();
        httpRequest.setAttribute("startTime", startTime);

        // 对于POST请求，先进行签名验证
        if ("POST".equalsIgnoreCase(method)) {
            if (!validateSignature(requestMap, httpResponse)) {
                // 签名验证失败，直接返回，不记录日志
                return;
            }
        }

        // 继续处理请求，确保使用RequestWrapper
        ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(httpResponse);
        try {
            chain.doFilter(requestWrapper, responseWrapper);
        } finally {
            // 请求处理完成后记录日志（签名验证成功的请求才记录）
            recordLog(requestWrapper, responseWrapper, startTime, requestMap, requestBody);
            // 复制响应内容到原始响应对象
            responseWrapper.copyBodyToResponse();
        }
    }

    /**
     * 验证请求签名
     * @param requestMap HTTP请求
     * @param response HTTP响应
     * @return 验证是否通过
     * @throws IOException IO异常
     */
    private boolean validateSignature(Map<String, Object> requestMap, HttpServletResponse response) throws IOException {
        String reqId =null;
        try {

            
            String appId = (String) requestMap.get("appId");
            reqId = (String) requestMap.get("reqId");
            String timestampStr = (String) requestMap.get("timestamp");
            String signature = (String) requestMap.get("sign");
            
            logger.info("请求参数: {appId={}, reqId={}, timestamp={}}", appId, reqId, timestampStr);
            
            if (appId == null || reqId == null || timestampStr == null || signature == null) {
                writeErrorResponse(response, ResultCode.PARAM_ERROR.getCode(), "缺少必要参数", reqId != null ? reqId : "");
                return false;
            }
            
//            long timestamp;
//            try {
//                timestamp = Long.parseLong(timestampStr);
//            } catch (NumberFormatException e) {
//                writeErrorResponse(response, ResultCode.PARAM_ERROR.getCode(), "时间戳格式错误", reqId);
//                return false;
//            }
//
//            long currentTime = System.currentTimeMillis();
//            if (Math.abs(currentTime - timestamp) > 300000) { // 5分钟有效期
//                writeErrorResponse(response, ResultCode.PARAM_ERROR.getCode(), "请求已过期", reqId);
//                return false;
//            }
            
            // 获取应用密钥
            String appSecret = getAppSecret(appId);
            if (appSecret == null) {
                writeErrorResponse(response, ResultCode.PARAM_ERROR.getCode(), "无效的应用ID", reqId);
                return false;
            }
            
            // 构建待签名字符串
            TreeMap<String, String> params = new TreeMap<>();
            for (Map.Entry<String, Object> entry : requestMap.entrySet()) {
                if (/*!"data".equals(entry.getKey()) &&*/!"sign".equals(entry.getKey()) && StrUtil.isNotEmpty(Convert.toStr(entry.getValue()))) {
                    params.put(entry.getKey(), entry.getValue().toString());
                }
            }


            // 验证签名
            if (!SM3SignatureUtil.verifySignature(params, appSecret, signature)) {
                writeErrorResponse(response, ResultCode.SIGNATURE_ERROR.getCode(), "签名验证失败", reqId);
                return false;
            }
            
            logger.debug("签名验证通过，appId: {}, reqId: {}", appId, reqId);
            return true;
        } catch (Exception e) {
            logger.error("签名验证异常", e);
            writeErrorResponse(response, ResultCode.FAILURE.getCode(), "签名验证异常: " + e.getMessage(), reqId != null ? reqId : "");
            return false;
        }
    }
    

    /**
     * 获取应用密钥
     * @param appId 应用ID
     * @return 应用密钥
     */
    private String getAppSecret(String appId) {
        try {
            return EntityCacheConfig.getJkmy(appId,jdbcTemplate);
        } catch (Exception e) {
            logger.error("获取应用密钥失败，appId: {}", appId, e);
            return null;
        }
    }



    /**
     * 写入错误响应
     * @param response HTTP响应
     * @param code 错误码
     * @param msg 错误消息
     * @param reqId 请求ID
     * @throws IOException IO异常
     */
    private void writeErrorResponse(HttpServletResponse response, Integer code, String msg, String reqId) throws IOException {
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType("application/json;charset=UTF-8");
        DrugResponseBase errorResponse = new DrugResponseBase();
        errorResponse.setCode(code);
        errorResponse.setMsg(msg);
        errorResponse.setReqId(reqId);
        String json = JSONUtil.toJsonStr(errorResponse);
        response.getWriter().write(json);
    }
    
    /**
     * 记录日志
     * @param request HTTP请求
     * @param response HTTP响应
     * @param startTime 开始时间
     */
    private void recordLog(HttpServletRequest request, ContentCachingResponseWrapper response, long startTime,Map<String, Object> requestMap,String requestBody) {
        try {
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;
            
            DevLog devLog = new DevLog();
            devLog.setId(IdWorker.getIdStr());
            devLog.setCategory("JIEKOU");

            devLog.setReqUrl(request.getRequestURL().toString());
            String ip = CommonIpAddressUtil.getIp(request);
            devLog.setOpIp(ip);
            devLog.setOpAddress(CommonIpAddressUtil.getCityInfo(ip));
            devLog.setCreateTime(new Timestamp(System.currentTimeMillis()));
            devLog.setCreateUser("system");
            devLog.setUpdateTime(new Timestamp(System.currentTimeMillis()));
            devLog.setUpdateUser("system");
            devLog.setOpTime(new Timestamp(System.currentTimeMillis()));

            int status = response.getStatus();
            devLog.setExeStatus(status >= 200 && status < 300 ? "SUCCESS" : "FAILURE");
            devLog.setExeMessage("接口调用完成，状态码: " + status + "，耗时: " + duration + "ms");
            // 正确获取响应数据
            byte[] responseArray = response.getContentAsByteArray();
            String responseStr = new String(responseArray, response.getCharacterEncoding());
            devLog.setResultJson(responseStr);
            // 获取请求参数
            String appId = (String) requestMap.get("appId");
            String reqId = (String) requestMap.get("reqId");
            devLog.setReqMethod(reqId);
            devLog.setParamJson(requestBody);
            devLog.setQyid(appId);
            Entity entityMe = EntityCacheConfig.ENTITY_CACHE.get(appId);
            devLog.setClassName(entityMe.getQymc());
            devLog.setDqbm(entityMe.getDqbm());
            // 获取接口名称（简化处理）
            String uri = request.getRequestURI();
            if (uri.contains("/getDrugList")) {
                devLog.setName("获取药品标准库数据");
                devLog.setMethodName("getDrugList");
            } else if (uri.contains("/uploadDrugInfo")) {
                devLog.setName("新增药品信息");
                devLog.setMethodName("uploadDrugInfo");
            } else if (uri.contains("/uploadDrugInvinfo")) {
                devLog.setName("库存信息上传");
                devLog.setMethodName("uploadDrugInvinfo");
            }  else if (uri.contains("/uploadDrugIn")) {
                devLog.setName("上传药品入库信息");
                devLog.setMethodName("uploadDrugIn");
            } else if (uri.contains("/uploadDrugOut")) {
                devLog.setName("上传药品出库信息");
                devLog.setMethodName("uploadDrugOut");
            }else if (uri.contains("/uploadHumitureInfo")) {
                devLog.setName("温湿度信息上传");
                devLog.setMethodName("uploadHumitureInfo");
            }else if (uri.contains("/editSubStoreInfo")) {
                devLog.setName("门店基本信息完善");
                devLog.setMethodName("editSubStoreInfo");
            }else if (uri.contains("/editSubStore")) {
                devLog.setName("维护下属门店信息");
                devLog.setMethodName("editSubStore");
            }else {
                devLog.setName(uri);
            }
            
            devLogService.saveLog(devLog);
        } catch (Exception e) {
            logger.error("记录日志时发生异常", e);
        }
    }
}